package com.distributed.dispatchserver;

import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.UnknownHostException;
import java.util.List;

import org.apache.log4j.Logger;
import org.apache.log4j.PropertyConfigurator;
import org.apache.mina.core.filterchain.DefaultIoFilterChainBuilder;
import org.apache.mina.core.session.IoSession;
import org.apache.mina.filter.codec.ProtocolCodecFilter;
import org.apache.mina.filter.codec.textline.TextLineCodecFactory;
import org.apache.mina.filter.logging.LoggingFilter;
import org.apache.mina.transport.socket.SocketAcceptor;
import org.apache.mina.transport.socket.nio.NioSocketAcceptor;

import com.distributed.common.Address;
import com.distributed.common.EventHandler;
import com.distributed.common.message.GetLoginServMessage;
import com.distributed.common.message.LoginServInfoMessage;
import com.distributed.util.HashUtils;

public class DispatchServer {
	private final static int LEASE = 3600 * 24;
	static {
		PropertyConfigurator.configure("log4j.properties");
	}
	private static Logger logger = Logger.getLogger(DispatchServer.class);

	private static int port = 1234;
	
	private static List<Address> address_list = null;
	
	private static void initServer() {
		DispatchConfiguration.load();
		String hostIp = DispatchConfiguration.getHostIp();
		int mport = DispatchConfiguration.getHostPort();
		if (mport != -1) {
			port = mport;
		}
		if (hostIp == null) {
			try {
				hostIp = InetAddress.getLocalHost().getHostAddress();
			} catch (UnknownHostException e) {
				e.printStackTrace();
			}
			logger.info("Host: " + hostIp + ":" + port);
		} else {
			logger.info("Host: " + hostIp + ":" + port);
		}

		logger.info("server list:");
		address_list = DispatchConfiguration.getServerList();
		if (address_list == null || address_list.size() == 0) {
			logger.warn("Address list is empty");
			return;
		}
		for (Address address : address_list) {
			logger.info(address.toString());
		}
	}

	private static void startServer() throws Exception {
		SocketAcceptor acceptor = new NioSocketAcceptor();
		
		DefaultIoFilterChainBuilder chain = acceptor.getFilterChain();
		chain.addLast("codec", new ProtocolCodecFilter(new TextLineCodecFactory()));
		chain.addLast("logger", new LoggingFilter());
		
		DispatchProtocolHandler dispatchProtocolHandler = new DispatchProtocolHandler();
		dispatchProtocolHandler.setHandler(dispatchEventHandler);
		acceptor.setHandler(dispatchProtocolHandler);
		acceptor.setReuseAddress(true);
		acceptor.bind(new InetSocketAddress(port));

		logger.info("Listening on Port: " + port);

		while (true) {
			logger.info("R: "
					+ acceptor.getStatistics().getReadBytesThroughput()
					+ " W: "
					+ acceptor.getStatistics().getWrittenBytesThroughput());
			Thread.sleep(60000);
		}
	}

	public static void main(String[] args) throws Exception {
		logger.info("Dispatch Server");

		initServer();
		startServer();
	}

	private static EventHandler dispatchEventHandler = new EventHandler() {

		public String process(IoSession ioSession, String request) {
			GetLoginServMessage getLoginMessage = GetLoginServMessage.getFromString(request);
			if(getLoginMessage == null) {
				return LoginServInfoMessage.ERROR;
			}
			
			Address address = HashUtils.HashRandom(address_list);
			if (address == null) {
				return new LoginServInfoMessage().toString();
			}
			
			return new LoginServInfoMessage(address.getIp(), address.getPort(), LEASE).toString();
		}

		public void closeSession(IoSession ioSession) {
			
		}
	};
}
